﻿using System;
using System.Reflection;
using System.Collections;
using Genko.Model.Schema;
using Genko.Model.Resources;
using Genko.Model.Collections;

namespace Genko.Model
{
	/// <summary>
	/// Represents a Member of a Model.
	/// </summary>
	public class Member : ICloneable
	{
		protected internal const string GUID_ATTRIBUTE = "GUID";
		protected internal static string GUID_FORMAT = GenkoModelRes.MemberGuidFormat;
	
		protected internal Guid guid = Guid.Empty;
		protected internal Member owner = null;
		protected MemberList _memberList = null;
		private ValidationState validationState = ValidationState.Unknown;

		/// <summary>
		/// Default constructor
		/// </summary>
		protected internal Member()
		{
			Initialize(null);
		}

		protected internal Member(Member owner)
		{
			Initialize(owner);
		}
		
		protected virtual void Initialize(Member owner)
		{
			this.guid = Guid.NewGuid();
			this.owner = owner;
		}
		
		/// <summary>
		/// The Owner of this Member
		/// </summary>
		public virtual Member Owner
		{
			get { return this.owner; }
		}
		
		/// <summary>
		/// Gets the Scheme to which this Member belongs.
		/// </summary>
		public virtual Scheme Scheme
		{
			get {
				if (this is Scheme) {
					return (Scheme)this;
				}
				return this.Owner.Scheme;
			}
		}

		/// <summary>
		/// Gets the attached members to this Member
		/// </summary>
		public virtual MemberCollection Members
		{
			get
			{
				return this.MemberList;
			}
		}

		/// <summary>
		/// Gets the tag name of the defining Member.
		/// This name is associated directly with the type of member.
		/// For example the name of a MetaModel member will be "MetaModel".
		/// In other cases the name of the member could be variable per type, as in a Model member,
		/// the name will be the name of its MetaModel.
		/// </summary>
		public virtual string TagName {
			get {
				return this.GetType().Name;
			}
		}

		public virtual string Value
		{
			get {
				return null;
			}
			set {
				throw new MemberException();
			}
		}
		
		/// <summary>
		/// Returns the GUID that identifies this member, which must be unique per Scheme
		/// </summary>
		/// <exception cref="Genko.Model.MemberException">If the GUID set is readonly or the scheme detects a collision</exception>
		[MemberAttribute]
        public virtual Guid GUID {
			get {
				return this.guid;
			}
			set {
				throw new MemberException();
			}
		}
		
		/// <summary>
		/// Returns ths ID of this member, which must be unique per Owner
		/// </summary>
        [MemberAttribute]
        public virtual string MemberID {
			get {
				return this.GUID.ToString(GUID_FORMAT);
			}
		}

        /// <summary>
        /// Returns the index of this member, or -1 if it's not attached
        /// </summary>
        [MemberAttribute]
        public virtual int MemberIndex
        {
            get { return Owner.IndexOfMember(this.MemberID); }
        }

		/// <summary>
		/// Gets the attached member at the given index
		/// </summary>
		/// <param name="index">The index of the attached member</param>
		/// <returns>The attached member at the given index</returns>
		public virtual Member this[int index]
		{
			get {
				return MemberList[index];
			}
			set {
				// TO-DO
			}
		}

		/// <summary>
		/// Gets the attached member with the given ID
		/// </summary>
		/// <param name="memberId">The member ID</param>
		/// <returns>The attached member with the specified ID</returns>
		public virtual Member this[string memberId]
		{
			get {
				return MemberList[memberId];
			}
			set {
				throw new MemberException();
			}
		}

		/// <summary>
		/// Returns true it this member is attached to its owner, false otherwise.
		/// </summary>
		public virtual bool IsAttached
		{
			get {
				return (Owner != null) && Owner.IsMemberAttached(this);
			}
		}

		/// <summary>
		/// Returns the validation state of the semantic rules of this Member
		/// </summary>
		public virtual ValidationState ValidationState
		{
			get {
				return validationState;
			}
			protected internal set {
				validationState = value;
			}
		}
		
		/// <summary>
		/// Gets the first attached member to this member, or null if there's no such
		/// </summary>
		public virtual Member FirstMember
		{
			get {
				if (_memberList == null) {
					return null;
				}
				return MemberList.FirstMember;
			}
		}
		
		/// <summary>
		/// Gets the last attached member to this member, or null if there's no such
		/// </summary>
		public virtual Member LastMember
		{
			get {
				if (_memberList == null) {
					return null;
				}
				return MemberList.LastMember;
			}
		}
		
		public virtual bool HasMembers
		{
			get {
				return _memberList != null && MemberList.Count > 0;
			}
		}

		public virtual MemberAttributes Attributes
		{
			get {
				return this.CreateAttributes();
			}
		}

		/// <summary>
		/// Gets the MemberList of attached members
		/// </summary>
		protected internal MemberList MemberList
		{
			get {
				if (_memberList == null) {
					_memberList = CreateMemberList();
				}
				return _memberList;
			}
		}

		/// <summary>
		/// Creates the MemberList of attached members
		/// </summary>
		/// <returns>The MemberList of attached members</returns>
		protected virtual MemberList CreateMemberList()
		{
			return new DefaultMemberList();
		}
		
		
		protected virtual MemberAttributes CreateAttributes()
		{
			MemberAttributeDictionary attributes = new MemberAttributeDictionary();
			PropertyInfo[] props = this.GetType().GetProperties();
			foreach (PropertyInfo property in props) {
				if (Attribute.IsDefined(property, typeof(MemberAttributeAttribute), true)) {
					MemberAttributeAttribute mAttr = (MemberAttributeAttribute)Attribute.GetCustomAttribute(property, typeof(MemberAttributeAttribute));
					MemberAttribute attribute = CreateAttribute(mAttr, property);
					attributes.Add(attribute);
				}
			}
			return attributes;
		}
		
		protected virtual MemberAttribute CreateAttribute(MemberAttributeAttribute metaAttribute, PropertyInfo propertyInfo)
		{
			// TO-DO
			return null;
		}

		public virtual bool HasAttribute(string name)
		{
			return false;
		}
		
		public virtual DataType GetAttributeType(string name)
		{
			return null;
		}
		
		public virtual string GetAttribute(string name)
		{
			return null;
		}

		public virtual void SetAttribute(string name, string value)
		{

		}

		/// <summary>
		/// Resturns true if the specified member is attached to this member
		/// </summary>
		/// <param name="member">the member to test</param>
		/// <returns>true if the specified member is attached to this member</returns>
		public virtual bool IsMemberAttached(Member member)
		{
			return MemberList.Contains(member);
		}

		/// <summary>
		/// Creates a new member owned by this member based on the specified tag name
		/// </summary>
		/// <param name="memberName">The tag name of the member</param>
		/// <returns>The member created</returns>
		public virtual Member CreateMember(string memberTagName)
		{
			MethodInfo[] methods = this.GetType().GetMethods();
			foreach (MethodInfo method in methods) {
				if (Attribute.IsDefined(method, typeof(MemberActivatorAttribute), true)) {
					MemberActivatorAttribute mAttr = (MemberActivatorAttribute) Attribute.GetCustomAttribute(method, typeof(MemberActivatorAttribute));
					if (string.Equals(mAttr.MemberName, memberTagName)) {
						return (Member)method.Invoke(this, null);
					}
				}
			}
			throw new MemberException("Can't find any member activator for tag name: " + memberTagName);
		}

		/// <summary>
		/// Tests if the specified member can be attached to this member
		/// </summary>
		/// <param name="member">the member to test</param>
		/// <returns>true if the member can be attached to this member</returns>
		public virtual bool CanBeAttached(Member member)
		{
			return true;
		}
		
		/// <summary>
		/// Clones this Member
		/// </summary>
		/// <returns>The clonned member</returns>
		public virtual object Clone()
		{
			return null; // TO-DO
		}

		/// <summary>
		/// Attachs the specified member to the end of the attached members, of this member.
		/// </summary>
		/// <param name="member">The member to attach</param>
		/// <returns>the attached member</returns>
		public virtual Member Attach(Member member)
		{
			if (!CanBeAttached(member)) {
				throw new MemberException();
			}
			MemberList.Add(member);
			return member;
		}

		/// <summary>
		/// Attachs the specified member to this member at the given index.
		/// </summary>
		/// <param name="memberIndex">The index</param>
		/// <param name="member">The member to attach</param>
		/// <returns>the attached member</returns>
		public virtual Member Attach(int memberIndex, Member member)
		{
			if (!CanBeAttached(member)) {
				throw new MemberException();
			}
			MemberList.Insert(memberIndex, member);
			return member;
		}

		/// <summary>
		/// Creates and attachs
		/// </summary>
		/// <param name="memberName"></param>
		/// <returns></returns>
		public virtual Member Attach(string memberName)
		{
			return Attach(CreateMember(memberName));
		}

		public virtual Member Detach(int memberIndex)
		{
			return null; // TO-DO
		}

		public virtual Member Detach(string memberId)
		{
			return null; // TO-DO
		}

		public virtual Member Detach(Member member)
		{
			return null; // TO-DO
		}

        public virtual int IndexOfMember(string memberId)
        {
            return -1; // TO-DO
        }

		/// <summary>
		/// Validates the semantic rules of this member
		/// </summary>
		/// <returns>true if this.ValidationState == ValidationState.Valid and all its attached members</returns>
		public virtual bool Validate()
		{
			IValidationNotifier notifier = Scheme.CreateValidationNotifier();
			if (!Validate(notifier)) {
				return false;
			}
			foreach (Member member in Members) {
				if (!member.Validate(notifier)) {
					return false;
				}
			}
			return true;
		}

		protected virtual void SetValidationState(ValidationState state)
		{
			ValidationState = state;
		}

		protected virtual bool Validate(IValidationNotifier notifier)
		{
			ValidationState = ValidationState.Valid;
			return true;
		}

		protected virtual ValidationEventArgs CreateValidationEventInfo(string message)
		{
			return new ValidationEventArgs(this, message, ValidationMessageType.Info);
		}

		protected virtual ValidationEventArgs CreateValidationEventWarn(string message)
		{
			return new ValidationEventArgs(this, message, ValidationMessageType.Warning);
		}

		protected virtual ValidationEventArgs CreateValidationEventError(string message)
		{
			return new ValidationEventArgs(this, message, ValidationMessageType.Error);
		}
	}
}
